use std::sync::Arc;

use axum::response::Html;
use axum_client_ip::SecureClientIpSource;
use sea_orm::DbConn;
use tera::Tera;
use tokio::sync::Mutex;

use crate::{error::AppResult, service::cache_service::ICacheService};

pub type ShareState = Arc<AppState>;

pub struct AppState {
    conn: DbConn,
    tera: Tera,
    tera_context: tera::Context,
    cache: Box<dyn ICacheService>,
    enforcer: Mutex<casbin::Enforcer>,
    ip_header: Option<SecureClientIpSource>,
}

impl AppState {
    pub fn new(
        conn: DbConn,
        tera: Tera,
        tera_context: tera::Context,
        cache: Box<dyn ICacheService>,
        enforcer: casbin::Enforcer,
        ip_header: Option<SecureClientIpSource>,
    ) -> Self {
        Self {
            conn,
            tera,
            tera_context,
            cache,
            enforcer: Mutex::new(enforcer),
            ip_header,
        }
    }

    /// 返回数据库
    pub fn db(&self) -> &DbConn {
        &self.conn
    }

    /// 返回缓存服务
    pub fn cache(&self) -> &Box<dyn ICacheService> {
        &self.cache
    }

    /// 生成模板
    pub fn render_template(
        &self,
        template_name: &str,
        context: &tera::Context,
    ) -> AppResult<Html<String>> {
        (&self.tera)
            .render(template_name, context)
            .map(Html)
            .map_err(Into::into)
    }

    pub fn tera_context(&self) -> tera::Context {
        self.tera_context.clone()
    }

    pub fn enforcer(&self) -> &Mutex<casbin::prelude::Enforcer> {
        &self.enforcer
    }

    pub fn ip_header(&self) -> Option<&SecureClientIpSource> {
        self.ip_header.as_ref()
    }

    pub fn set_cache(&mut self, cache: Box<dyn ICacheService>) {
        self.cache = cache;
    }

    pub fn set_enforcer(&mut self, enforcer: Mutex<casbin::Enforcer>) {
        self.enforcer = enforcer;
    }

    pub fn set_ip_header(&mut self, ip_header: Option<SecureClientIpSource>) {
        self.ip_header = ip_header;
    }
}
